use strict;
use warnings;

package My::Form::Helper::Processor;
use base qw(Exporter);
our @EXPORT_OK = qw(prev_form next_form);    # symbols to export on request
use My::Form::Helper::Tools
	qw( eval_value flatten_form_elements save_fields save_to_hash_from_RHTML_form);

sub next_form {
	my %params          = @_;
	my $RHTMLO_form     = $params{RHTMLO_form};
	my $elements        = $params{elements};
	my $session_hashref = $params{session_hashref};
	my $validate        = $params{validate};
	my $pre_save        = $params{pre_save};
	my $next_form       = $params{next_form};
	my $html            = '';

	# init variables
	if ( !$session_hashref->{previous_forms}
		 || ( ref( $session_hashref->{previous_forms} ) ne 'ARRAY' ) )
	{
		$session_hashref->{previous_forms} = [];
	}

	# calculate form multiplicity (number of the same form in a sequence)
	my $current_form_multiplicity = 1;    # the current form is the first of a sequence
	foreach my $back_form ( reverse( @{ $session_hashref->{previous_forms} } ) ) {
		if ( $back_form eq $session_hashref->{current_form} ) {
			$current_form_multiplicity++;
		} else {
			last;
		}
	}
	$session_hashref->{form_multiplicity} = $current_form_multiplicity;

	# reset session variables used by process_form
	delete $session_hashref->{error_fields};

	# Validate fields first
	my @element_list = flatten_form_elements( elements => $params{elements} );
	my @validation_failed;
	foreach my $element_params (@element_list) {

	# we skip validation for fields that are not displayed in the first place (ie: condition failed)
		if ( exists( $element_params->{field_name} )
			 && ( ( defined( $element_params->{condition} )
					&& eval_value( init_values => [ %{$session_hashref} ],
								   value       => $element_params->{condition} )
				  )
				  || ( !defined( $element_params->{condition} ) )
			 )
			 && !$RHTMLO_form->field( $element_params->{field_name} )->validate
			)
		{

			# record field name
			push @validation_failed, $element_params->{field_name};
		} ## end if ( exists( $element_params->{field_name}...
	} ## end foreach my $element_params (@element_list)
	$session_hashref->{error_fields} = [@validation_failed] if ( scalar(@validation_failed) );
	my %validated_save_to =
		save_to_hash_from_RHTML_form( RHTMLO_form      => $RHTMLO_form,
									  session_hashref  => $session_hashref,
									  validated_fields => 0,
									  elements         => $elements,
		);

	# then perform form validation
	my $rv = eval_value( init_values => [ %{$session_hashref}, %validated_save_to ],
						 value       => $validate );

	# if no field validation failed and form validation passed
	if ( ( ( defined($validate) && $rv ) || ( !defined($validate) ) )
		 && !scalar(@validation_failed) )
	{

		# if pre-save code exists
		if ($pre_save) {

			# we will save values saved to the @save_to array
			eval_value( init_values     => [ %{$session_hashref}, %validated_save_to ],
						session_hashref => $session_hashref,
						value           => $pre_save,
			);
		} ## end if ($pre_save)

		# then save field values to session
		save_fields( RHTMLO_form     => $RHTMLO_form,
					 elements        => $elements,
					 session_hashref => $session_hashref
		);

		# then evaluate the next form
		my $current_form = $session_hashref->{current_form};
		my $next_form =
			eval_value( init_values => [ %{$session_hashref}, %validated_save_to ],
						value       => $next_form );
		push @{ $session_hashref->{previous_forms} }, $current_form;
		$session_hashref->{current_form} = $next_form;
		if ( $next_form eq $current_form ) {
			$session_hashref->{form_multiplicity}++;
		} ## end if ( $next_form ne $current_form )
		return 1;
	} ## end if ( ( ( defined($validate) && $rv ) || ( ...
} ## end sub next_form

sub prev_form {
	my %params          = @_;
	my $RHTMLO_form     = $params{RHTMLO_form};
	my $elements        = $params{elements};
	my $session_hashref = $params{session_hashref};
	my $pre_save        = $params{pre_save};
	my $next_form       = $params{next_form};
	my $html            = '';

	# init variables
	if ( !$session_hashref->{previous_forms}
		 || ( ref( $session_hashref->{previous_forms} ) ne 'ARRAY' ) )
	{
		$session_hashref->{previous_forms} = [];
	}

	# calculate form multiplicity (number of the same form in a sequence)
	my $current_form_multiplicity = 1;    # the current form is the first of a sequence
	foreach my $back_form ( reverse( @{ $session_hashref->{previous_forms} } ) ) {
		if ( $back_form eq $session_hashref->{current_form} ) {
			$current_form_multiplicity++;
		} else {
			last;
		}
	}
	$session_hashref->{form_multiplicity} = $current_form_multiplicity;

	# reset session variables used by process_form
	delete $session_hashref->{error_fields};

	# Validate fields first
	my @element_list = flatten_form_elements( elements => $params{elements} );
	my %validated_save_to =
		save_to_hash_from_RHTML_form( RHTMLO_form      => $RHTMLO_form,
									  session_hashref  => $session_hashref,
									  validated_fields => 0,
									  elements         => $elements,
		);

	# if pre-save code exists
	if ($pre_save) {

		# we will save values saved to the @save_to array
		eval_value( init_values     => [ %{$session_hashref}, %validated_save_to ],
					session_hashref => $session_hashref,
					value           => $pre_save,
		);
	} ## end if ($pre_save)

	# then save field values to session
	save_fields( RHTMLO_form     => $RHTMLO_form,
				 elements        => $elements,
				 session_hashref => $session_hashref
	);

	# then evaluate the next form
	my $current_form = $session_hashref->{current_form};
	my $next_form    = $current_form;
	if ( scalar( @{ $session_hashref->{previous_forms} } ) ) {
		$next_form = pop( @{ $session_hashref->{previous_forms} } );
		if ( $next_form eq $current_form ) {
			$session_hashref->{form_multiplicity}--;
		} else {
			$session_hashref->{form_multiplicity} = 1;
		}
		$session_hashref->{current_form} = $next_form;
		return 1;
	}
} ## end sub next_form
1;
